/**
 * @file		spi.c
 * @brief		SPI
 * @note		None
 * @attention	None
 * 
 * <B><I>ALL RIGHTS RESERVED, COPYRIGHT&copy; SOCIONEXT INCORPORATED 2016</I></B>
 */

/* Standard includes. */
#include <stdio.h>

#include "dd_arm.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"

#include "dd_pmc_spi.h"
#include "spi.h"

#include "ipcu.h"

// Notification enable.
static volatile int iSPISendNotificationEnable = 0;
static volatile int iSPIReceiveNotificationEnable = 0;

// Buffer data.
static volatile T_SPI_SEND_BUFFER xSendBuffer = { 0, 0 };
static volatile T_SPI_RECEIVE_BUFFER xReceiveBuffer = { 0, 0, 0, 0 };

// Local function.
static void prvSPISendCallback(int result);
static void prvSPIReceiveCallback(int result);


/**
SPI Send callback function.
*/
static void prvSPISendCallback(int result)
{
	BaseType_t		xHigherPriorityTaskWoken, xResult;
	unsigned long	ulData[3];
	
	// Stop
	Dd_PMC_SPI_Stop();

	// Notification.
	if (iSPISendNotificationEnable) {
		xHigherPriorityTaskWoken = pdFALSE;
		
		// Set SPI Send Notify data
		ulData[0] = 0x02000005;
		ulData[1] = 0;
		if (result != D_DDIM_OK) {
			// Function Error
			ulData[2] = D_IPCU_ERROR_TYPE_FUNCTION_ERROR;
		}
		else {
			// OK
			ulData[2] = 0;
		}
		vIPCUSetNotifyCommand(ulData, 3);
		
		// Set Event Flag : SPI Send Notification.
		xResult = xEventGroupSetBitsFromISR(xNotifyEventFlag, (1 << NFY_SPI_SEND), &xHigherPriorityTaskWoken);
		if (xResult == pdPASS) {
			portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
		}
	}
}

/**
SPI Receive callback function.
*/
static void prvSPIReceiveCallback(int result)
{
	BaseType_t		xHigherPriorityTaskWoken, xResult;
	int				iRes = 0;
	unsigned long	ulData[3];
	
	// Stop
	Dd_PMC_SPI_Stop();
	
	// write pointer
	if (xReceiveBuffer.pointer != 0) {
		*((ULONG*)xReceiveBuffer.pointer) = xReceiveBuffer.address + xReceiveBuffer.size;
	}
	
	// Notification.
	if (iSPIReceiveNotificationEnable) {
		xHigherPriorityTaskWoken = pdFALSE;
		
		ulData[0] = 0x02000006;
		ulData[1] = 0;
		if (result != D_DDIM_OK) {
			// Function Error
			ulData[2] = D_IPCU_ERROR_TYPE_FUNCTION_ERROR;
		}
		else {
			// OK
			ulData[2] = 0;
		}
		vIPCUSetNotifyCommand(ulData, 3);
		
		// Set Event Flag : SPI Receive Notification.
		xResult = xEventGroupSetBitsFromISR(xNotifyEventFlag, (1 << NFY_SPI_RECEIVE), &xHigherPriorityTaskWoken);
		if (xResult == pdPASS) {
			portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
		}
	}
	
	switch (xReceiveBuffer.overlap) {
		case 1:		// 1:cyclic
			// Set receiving data.
			iRes = Dd_PMC_SPI_Set_Recv_Data((void*)xReceiveBuffer.address, xReceiveBuffer.size);
			if (iRes != D_DDIM_OK) {
				// failure.
				return;
			}
			
			// Start.
			iRes = Dd_PMC_SPI_Start_Recv((VP_PMC_SPI_CALLBACK)prvSPIReceiveCallback);
			if (iRes != D_DDIM_OK) {
				// failure.
				return;
			}
			break;
		case 2:		// 2:same position
			// Set receiving data.
			iRes = Dd_PMC_SPI_Set_Recv_Data((void*)(xReceiveBuffer.address + xReceiveBuffer.size - 1), 1);
			if (iRes != D_DDIM_OK) {
				// failure.
				return;
			}
			
			// Start.
			iRes = Dd_PMC_SPI_Start_Recv((VP_PMC_SPI_CALLBACK)prvSPIReceiveCallback);
			if (iRes != D_DDIM_OK) {
				// failure.
				return;
			}
			break;
		default:	// 0:no-overlap
			break;
	}
}

/**
SPI Send start.
@retval  0 : success
@retval  1 : parameter error
@retval  3 : function error
*/
int iSPISendStart(void)
{
	int					iRes = 0;
	T_DD_PMC_SPI_CTRL	xSPICtrl;
	
	if ((xSendBuffer.address == 0) || (xSendBuffer.size == 0)) {
		// parameter error.
		return 1;
	}
	
	// Set control.
	xSPICtrl.type				= E_DD_PMC_SPI_TYPE_MASTER;
	xSPICtrl.mode				= E_DD_PMC_SPI_MODE_3_CPOL1_CPHA1;
	xSPICtrl.enable_sig			= E_DD_PMC_SPI_ENABLE_SIG_CPU;
	xSPICtrl.bit_direction		= E_DD_PMC_SPI_BIT_DIR_MSB_FIRST;
	xSPICtrl.bit_length			= E_DD_PMC_SPI_BIT_LEN_8;
	xSPICtrl.clk_div			= 0;
	xSPICtrl.delay_en			= 0;
	xSPICtrl.delay_val			= 0;
	xSPICtrl.rx_inhibit_en		= 1;
	xSPICtrl.fifo_depth			= 16;
	xSPICtrl.fifo_wmark_tx		= 0;
	xSPICtrl.fifo_wmark_rx		= 8;
	xSPICtrl.dma_en				= 0;
	xSPICtrl.dma_recv_to		= 0;
	xSPICtrl.ss_info.cont_trans	= E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY;
	xSPICtrl.ss_info.ssout		= 1;
	xSPICtrl.ss_info.sspol		= 0;
	xSPICtrl.pcallback_ss		= NULL;
	iRes = Dd_PMC_SPI_Ctrl(&xSPICtrl);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	// Set send data.
	iRes = Dd_PMC_SPI_Set_Send_Data((void*)xSendBuffer.address, xSendBuffer.size);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	// Start.
	iRes = Dd_PMC_SPI_Start_Send((VP_PMC_SPI_CALLBACK)prvSPISendCallback);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	// success.
	return 0;
}

/**
SPI Receive start.
@retval  0 : success
@retval  1 : parameter error
@retval  3 : function error
*/
int iSPIReceiveStart(void)
{
	int					iRes = 0;
	T_DD_PMC_SPI_CTRL	xSPICtrl;
	
	if ((xReceiveBuffer.address == 0) || (xReceiveBuffer.size == 0)) {
		// parameter error.
		return 1;
	}
	
	// Set control.
	xSPICtrl.type				= E_DD_PMC_SPI_TYPE_MASTER;
	xSPICtrl.mode				= E_DD_PMC_SPI_MODE_3_CPOL1_CPHA1;
	xSPICtrl.enable_sig			= E_DD_PMC_SPI_ENABLE_SIG_CPU;
	xSPICtrl.bit_direction		= E_DD_PMC_SPI_BIT_DIR_MSB_FIRST;
	xSPICtrl.bit_length			= E_DD_PMC_SPI_BIT_LEN_8;
	xSPICtrl.clk_div			= 0;
	xSPICtrl.delay_en			= 0;
	xSPICtrl.delay_val			= 0;
	xSPICtrl.rx_inhibit_en		= 0;
	xSPICtrl.fifo_depth			= 16;
	xSPICtrl.fifo_wmark_tx		= 0;
	xSPICtrl.fifo_wmark_rx		= 8;
	xSPICtrl.dma_en				= 0;
	xSPICtrl.dma_recv_to		= 0;
	xSPICtrl.ss_info.cont_trans	= E_DD_PMC_SPI_CONT_TRANS_ACT_FIFO_EMPTY;
	xSPICtrl.ss_info.ssout		= 1;
	xSPICtrl.ss_info.sspol		= 0;
	xSPICtrl.pcallback_ss		= NULL;
	iRes = Dd_PMC_SPI_Ctrl(&xSPICtrl);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	iRes = Dd_PMC_SPI_Set_Recv_Data((void*)xReceiveBuffer.address, xReceiveBuffer.size);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	// Start.
	iRes = Dd_PMC_SPI_Start_Recv((VP_PMC_SPI_CALLBACK)prvSPIReceiveCallback);
	if (iRes != D_DDIM_OK) {
		// function error.
		return 3;
	}
	
	// success.
	return 0;
}

/**
SPI Receive stop.
@retval  0 : success
*/
int iSPIReceiveStop(void)
{
	// Stop.
	Dd_PMC_SPI_Stop();
	
	// normal end.
	return 0;
}

/**
set SPI Send notification.
@param [in]	enable		notification enable.<br>
						0:disable, 1:enable
@retval  0 : success
@retval  1 : parameter error
*/
int iSPISendNotification(int enable)
{
	if ((enable != 0) && (enable != 1)) {
		// parameter error.
		return 1;
	}
	
	// Set notification.
	iSPISendNotificationEnable = enable;
	
	// normal end.
	return 0;
}

/**
set SPI Receive notification.
@param [in]	enable		notification enable.<br>
						0:disable, 1:enable
@retval  0 : success
@retval  1 : parameter error
*/
int iSPIReceiveNotification(int enable)
{
	if ((enable != 0) && (enable != 1)) {
		// parameter error.
		return 1;
	}
	
	// Set notification.
	iSPIReceiveNotificationEnable = enable;
	
	// normal end.
	return 0;
}

/**
set SPI input buffer for send.
@param [in]	buff		Send buffer data.
@retval  0 : success
@retval  1 : parameter error
*/
int iSPISetSendBuffer(T_SPI_SEND_BUFFER const* const buff)
{
	if (buff == 0) {
		// parameter error.
		return 1;
	}
	
	xSendBuffer = *buff;
	
	// success.
	return 0;
}

/**
set SPI input buffer for receive.
@param [in]	buff		Receive buffer data.
@retval  0 : success
@retval  1 : parameter error
*/
int iSPISetReceiveBuffer(T_SPI_RECEIVE_BUFFER const* const buff)
{
	if (buff == 0) {
		// parameter error.
		return 1;
	}
	
	xReceiveBuffer = *buff;
	
	// normal end.
	return 0;
}

